#include "Shader.h"

namespace egg{

    Shader::Shader(){
        mID=glCreateProgram();
        mVertShader=glCreateShader(GL_VERTEX_SHADER);
        mFragShader=glCreateShader(GL_FRAGMENT_SHADER);
        glAttachShader(mID,mVertShader);
        glAttachShader(mID,mFragShader);
    }

    Shader::~Shader(){
        glDetachShader(mID,mVertShader);
        glDetachShader(mID,mFragShader);
        glDeleteShader(mVertShader);
        glDeleteShader(mFragShader);
        glDeleteProgram(mID);
    }

    void Shader::fromFile(const string& vs,const string& fs){
        FileStream vsFile(vs);
        FileStream fsFile(fs);
        string vertSrc=vsFile.getAsString();
        string fragSrc=fsFile.getAsString();
        compileAndLink(vertSrc,fragSrc);
    }

    void Shader::fromString(const string& vs,const string& fs){
        compileAndLink(vs,fs);
    }

    void Shader::_bindParamMap(ShaderParamMap* paramMap){
        mParamMaps.push_back(paramMap);
    }

    void Shader::_unbindParamMap(ShaderParamMap* paramMap){
        vector<ShaderParamMap*>::iterator itr=mParamMaps.begin();
        for(;itr!=mParamMaps.end();itr++){
            if((*itr)==paramMap){
                mParamMaps.erase(itr);
                return;
            }
        }
    }

    void Shader::compileAndLink(const string& vert,const string& frag){
        if(vert==""||frag=="")
            LogMgr->logMessage("shader source empty");
        compile(mVertShader,vert);
        compile(mFragShader,frag);
        link();
        for(int i=0;i<mParamMaps.size();i++){
            mParamMaps[i]->_updateParams();
        }
    }

    void Shader::compile(GLuint shader,const string& src){
        const char* str=src.c_str();
        glShaderSource(shader,1,&str,0);
        glCompileShader(shader);
        int state;
        glGetShaderiv(shader,GL_COMPILE_STATUS,&state);
        if(state==GL_FALSE){
            string result;
            int infoLength = 0;
            int charsWritten = 0;
            char* infoLog;
            glGetShaderiv(shader,GL_INFO_LOG_LENGTH,&infoLength);
            if (infoLength > 0) {
                char* infoLog = new char[infoLength+1];
                glGetShaderInfoLog(shader, infoLength, &charsWritten, infoLog);
                infoLog[infoLength]=0;
                result=infoLog;
                LogMgr->logMessage(getCompiledSource(shader));
                LogMgr->logMessage(result);
                delete[] infoLog;
            }
        }
    }

    void Shader::link(){
        glBindAttribLocation(mID,VA_POSITION,"atr_vec3_position");
        glBindAttribLocation(mID,VA_TEXCOORD0,"atr_vec2_texcoord0");
        glBindAttribLocation(mID,VA_TEXCOORD1,"atr_vec2_texcoord1");
        glBindAttribLocation(mID,VA_NORMAL,"atr_vec3_normal");
        glBindAttribLocation(mID,VA_TANGENT,"atr_vec3_tangent");
        glBindAttribLocation(mID,VA_COLOR0,"atr_vec4_color0");
        glBindAttribLocation(mID,VA_COLOR1,"atr_vec4_color1");
        glLinkProgram(mID);
        RHILogError("glLinkProgram");
    }

    string Shader::getCompiledSource(GLuint shader){
        GLsizei srcLen;
        size_t bufSize=10240;
        char* buf=new char[bufSize];
        glGetShaderSource(shader,bufSize,&srcLen,buf);
        buf[srcLen]=0;
        string ret(buf);
        delete[] buf;
        return ret;
    }

}